<template>
  <div id="svgContainer">
    <svg id="svgGraph" ref="linkGraph" width="100%" height="100%">
       <defs>
        <pattern id="light" x="0" y="0" height="1" width="1">
          <image :width="pr*2" :height="pr*2" xlink:href="../../assets/images/light.png"></image>
        </pattern>
        <pattern id="flowPoint" x="0" y="0" height="1" width="1">
          <image width="36" height="43" xlink:href="../../assets/images/flowPoint.png"></image>
        </pattern>
        <pattern id="dark" x="0" y="0" height="1" width="1">
          <image :width="pr*2" :height="pr*2" xlink:href="../../assets/images/dark.png"></image>
        </pattern>
        <pattern id="blue" x="0" y="0" height="1" width="1">
          <image :width="cr*2" :height="cr*2" xlink:href="../../assets/images/blue.png"></image>
        </pattern>
        <pattern id="green" x="0" y="0" height="1" width="1">
          <image :width="cr*2" :height="cr*2" xlink:href="../../assets/images/green.png"></image>
        </pattern>
        <pattern id="orange" x="0" y="0" height="1" width="1">
          <image :width="cr*2" :height="cr*2" xlink:href="../../assets/images/orange.png"></image>
        </pattern>
        <pattern id="red" x="0" y="0" height="1" width="1">
          <image :width="cr*2" :height="cr*2" xlink:href="../../assets/images/red.png"></image>
        </pattern>
        <!-- <pattern id="ginger" x="0" y="0" height="1" width="1">
            <image :width="er*2" :height="er*2" xlink:href="../../assets/images/ginger.png"></image>
        </pattern> -->
       </defs> 
    </svg>
  </div>
</template>

<script>
import { res0, res1 } from '../../common/common';
export default {
  data() {
    return {
      r: 280, // 外围圆圈半径
      nodes: [], // 节点
      drawBoard: null, 
      animateTime: 2000,
    }
  },
  computed: {
    scale() { // 根据屏幕大小自动缩放比例
      return document.body.offsetWidth / 1920;
    },
    centerX() {
      return document.getElementById('svgContainer').offsetWidth / 2;
    },
    centerY() {
      return document.getElementById('svgContainer').offsetHeight / 2 + 50;
    },
    pr() { // 父节点半径
      return 56 * this.scale;
    },
    cr() { // 子节点半径
      return 28 * this.scale;
    }
  },
  mounted() {
    // 初始化
    this.init();
  },
  methods: {
    init() {
      this.drawBoard = this.$d3.select('#svgGraph');
      this.getNodes();
      // 绘制外围圆圈路径
      this.drawCirclePath();
      // 绘制主节点
      this.drawNodes();
      // 绘制各节点之间的连线
      this.drawLines();
      // 绘制链路运动动画
      this.drawFlowpoint();
    },
    // 获取节点信息
    getNodes() {
      let nodes = [];
      res0.forEach(item => {
        if(item.parentId !== '0') {
          item.xaxis = this.scale * Number(item.xaxis);
          item.yaxis = this.scale * Number(item.yaxis);
          nodes.push(item);
        }
      });
      console.log('res0===', res0);
      // console.log('res1===', res1);
      this.nodes = nodes;
    },
    // 绘制圆圈路径
    drawCirclePath() {
      const d = this.ellipseToPath(0, 0, this.r, this.r);
      const container = this.drawBoard.append('g')
      .attr('id', 'gEllipse')
      .attr('transform', `translate(${this.centerX}, ${this.centerY})`);

      container.append('path')
      .attr('id', 'ellipse')
      .attr('d', d)
      .style('stroke', '#fff')
      .style('opacity', '0.3')
      .style('stroke-width', 1)
      .style('fill', 'transparent');

      // 绘制沿着圆路径运动的物体
      const ball = container.append('g')
      .attr('id', 'ellipseBall0')
      .attr('transform', 'rotate(0)')
      .attr('class', 'ellipse-ball child-node-member')
      .style('cursor', 'pointer');

      const ballCircle = ball.append('circle')
      .attr('r', 18)
      .attr('id', "arc")
      .attr('transform', 'rotate(70)')
      .attr('fill', 'url(#flowPoint)')
      .attr('class','child-node-member');

      // 椭圆轨迹动画
      ballCircle.append('animateMotion')
      .attr('path', d)
      .attr('rotate', 'auto')
      .attr('dur', '60s')
      .attr('stroke-dashoffset', '20')
      .attr('repeatCount', 'indefinite');
    },
    // 绘制节点
    drawNodes() {
      // r - 大圈半径 cr - 大球半径 pr - 小球半径
      const { nodes, centerX, centerY, r, cr, pr } = this;
      let nodeGroup = this.drawBoard.append('g').attr('class', 'nodeParent');
      nodes.forEach(item => {
        let x = centerX;
        let y = centerY;
        if(item.parentName !== '运输') {
          // (2 * Math.PI / 360)转换为弧度
          const idx = nodes.filter(val=>(val['parentName']!=='运输')).map(node=>(node['parentName'])).indexOf(item['parentName']);
          const deg = (2 * Math.PI / 360) * (360 / (nodes.length - 1) * idx + 180);
          x = centerX + Math.sin(deg) * r;
          y = centerY + Math.cos(deg) * r;
        }

        const group = nodeGroup.append('g')
          .attr('id', `node_${item.parentId}`)
          .attr('axisX', x)
          .attr('axisY', y)
          .attr('transform', `translate(${x},${y})`)
          .style('cursor', 'pointer');

        const animateD = this.ellipseToPath(0, 0, cr + pr, cr + pr);
        group.append('path')
          .attr('id', `node_${item.parentId}`)
          .attr('d', animateD)
          .attr('stroke', 'transparent')
          .attr('fill', 'transparent');

        group.append('circle')
          .attr('class','parent-node-control')
          .attr('id', 'node-wrap-'+item.parentId)
          .attr('r', this.pr + 2*this.cr)
          .attr('fill', 'transparent')
          .on('click', function() {});

        group.append('circle')
          .attr('r', this.pr)
          .attr('fill', '#0A1870');

        group.append('circle')
          .attr('class', 'node-circle')
          .attr('r', pr)
          .attr('fill', 'url(#dark)');

        group.append('text')
          .attr('text-anchor', 'middle')
          .attr('class', 'parent-node-member')
          .attr('dy', '0.4em')
          .attr('font-size', 24)
          .text(item.parentName)
          .style('fill', 'white');
        // 绘制子节点
        this.drawChildrenNodes(item);
      });
    },
    // 绘制子节点
    drawChildrenNodes(parentNode) {
      console.log('parentNode===', parentNode);
      const { pr, cr } = this;
      const { sonInfo} = parentNode;
      const childNode = this.drawBoard.select(`#node_${parentNode.parentId}`);
      sonInfo.forEach((item, index) => {
        const angle = Math.PI / 180 * index * 360 / sonInfo.length;
        const nr = pr + cr;
        const x = Math.sin(angle) * nr,
        y = Math.cos(angle) * nr;
        const n = childNode.append('g')
          .attr('id', `child_node_${index}`)
          .attr('class', 'child_node')
          .attr('transform', `translate(${x}, ${y})`);
      });
    },
    // 绘制各节点之间的连线
    drawLines() {
      const lineContainer = this.drawBoard.append('g').attr('class', 'linesContainer')
      this.getAllLines().then(res => {
        res.forEach(item => {
          const { start, end } = item;
          lineContainer.append('line')
          .attr('x1', start.x)
          .attr('y1', start.y)
          .attr('x2', end.x)
          .attr('y2', end.y)
          .style('stroke', '#fff')
          .style('stroke-width', 1)
          .style('opacity', 0.3)
        });
      });
    },
    // 光点流向
    drawFlowpoint() {
      const flowPointContainer = this.drawBoard.append('g').attr('class', 'flow-point-container');
      this.getAllLines().then(res => {
        res.forEach(item => {
          const { start, end, attribute, delay, waitingTime, duration } = item;
          const flowPoint = flowPointContainer.append('g')
            .style('opacity', 0)
            .attr('transform', `translate(${start.x},${start.y})`);
          // 绘制链路上的光点
          const flowPointCircle = flowPoint.append('circle')
            .attr('r', 18)
            .attr('fill', 'url(#flowPoint)')
            .style('transform', 'rotate(0)');
          // 绘制光点运动时的线条
          const animatePath = flowPointContainer.append('line')
            .attr('x1', start.x)
            .attr('y1', start.y)
            .attr('x2', start.x)
            .attr('y2', start.y)
            .style('stroke', '#fff')
            .style('stroke-width', 3);
          
          const run = (delayTime) => {
            // 链路动画
            animatePath.transition()
              .ease(this.$d3.easeLinear)
              .duration(duration)
              .delay(delayTime ? delayTime : delay)
              .attr('x2', end.x)
              .attr('y2', end.y)
              .on('end', () => {
                  animatePath
                  .attr('x2', start.x)
                  .attr('y2', start.y)
              });
            // 光点动画
            flowPoint.transition()
              .ease(this.$d3.easeLinear)
              .duration(duration)
              .delay(delayTime ? delayTime : delay)
              .attr('transform', `translate(${end.x}, ${end.y})`)
              .on('start', () => {
                flowPoint.style('opacity', 1);
              })
              .on('end', () => {
                // flowPoint.style('opacity', 0);
                // 判断光点是否返回 attribute
                if(waitingTime === 0 && attribute !== '1') { // 不需要等待，动画到头就重复开始
                  flowPoint.attr('transform', `translate(${start.x}, ${start.y})`);
                  run();
                } else if(waitingTime === 0 && attribute === '1') { // 不需要等待，按原路线返回
                  back(() => {
                    flowPoint.style('opacity', 0);
                    run(delay * 2);
                  });
                } else if(attribute === '1') {
                  waiting(() => {
                    flowPoint.style('opacity', 1);
                    back(run);
                  });
                } else {
                  waiting(() => {
                    flowPoint
                    .attr('transform', `translate(${start.x}, ${start.y})`);
                    run(delay * 2);
                  });
                }
              })
          }
          // 返回操作
          const back = (cb) => {
            // 链路动画
            animatePath.transition()
              .ease(this.$d3.easeLinear)
              .duration(duration)
              .attr('x1', start.x)
              .attr('y1', start.y)
              .on('start', () => {
                animatePath
                  .attr('x1', end.x)
                  .attr('y1', end.y)
                  .attr('x2', end.x)
                  .attr('y2', end.y)
              })
              .on('end', () => {
                  animatePath
                  .attr('x2', start.x)
                  .attr('y2', start.y)
              });
            
            flowPoint.transition()
              .ease(this.$d3.easeLinear)
              .duration(duration)
              .attr('transform', d => {
                return `translate(${start.x}, ${start.y})`;
              })
              .on('start', () => {
                // flowPoint.style('opacity', 1);
              })
              .on('end', () => {
                // flowPoint.style('opacity', 0);
                cb && cb();
              })
          };
          // 隐藏光点
          const waiting = (cb) => {
              item.timer = null;
              flowPoint.style('opacity', 0);
              item.timer = setTimeout(() => {
                  if (cb) {
                      cb();
                  }
              }, waitingTime);
          }
          run();
        });
      });
    },
    // 获取所有连线信息
    getAllLines() {
      return new Promise(resolve => {
        let arr = [];
        res1.forEach(item => {
          const { path, attribute, delay, duration, waiting } = item;
          const sourceNode = this.$d3.select(`#node_${path[0]}`);
          const targetNode = this.$d3.select(`#node_${path[1]}`);
          const x1 = Number(sourceNode.attr('axisX')),
          y1 = Number(sourceNode.attr('axisY')),
          x2 = Number(targetNode.attr('axisX')),
          y2 = Number(targetNode.attr('axisY'));
          const a = x2 - x1,b = y2 - y1;
          // 求圆半径 用的勾股定理 （在不知道半径的情况下可以用）
          // const edgeLength = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
          // pr - 每个大球的半径 r - 整个大圆的半径
          // 正弦定理：直角三角形中对边的值比上斜边的值 正弦值sinA = b / this.r; 
          // 余弦定理：直角三角形中邻边的值比上斜边的值 余弦值cosA = a / this.r;
          // 大球的半径已知 那两个大球连线的点的所在的
          // x: a / this.r = x / this.pr ==>  x = a / this.r * this.pr
          // y: b / this.r = y / this.pr ==> y = b / this.r * this.pr
          // 计算每个小圆的x,y长度，用以绘制线条，防止线条穿过小圆
          const a1 = a / this.r * this.pr,
          b1 = b / this.r * this.pr;

          arr.push({
            sourceId: path[0],
            targetId: path[1],
            delay: this.animateTime * Number(delay), // 动画延迟事件
            duration: this.animateTime * Number(duration), // 动画持续时间
            waitingTime: this.animateTime * Number(waiting),
            attribute,
            start: {
              x: x1 + a1,
              y: y1 + b1,
            },
            end: {
              x: x2 - a1,
              y: y2 - b1
            }
          })
          resolve(arr);
        });
      })
    },
    // 圆路径计算
    ellipseToPath(cx, cy, rx, ry) {
      //非数值单位计算，如当宽度像100%则移除
      if (isNaN(cx - cy + rx - ry)) return;
      let path =
          'M' + (cx-rx) + ' ' + cy +
          'a' + rx + ' ' + ry + ' 0 0 1 ' + 2*rx + ' 0' +
          'a' + rx + ' ' + ry + ' 0 0 1 ' + (-2*rx) + ' 0' +
          'z';
      return path;
    },
  }
}
</script>

<style scoped>
#svgContainer{
  background: url('../../assets/images/bg.png') no-repeat;
  height: 100vh;
  overflow: auto;
}
</style>